using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class CompositeTermBuilderSpecification {
        class FakeTerm : Term {
            public FakeTerm () : base (null) {}
        }

        [Test]
        public void ShouldAddMultiplesForThoseInMultiplesSet () {
            var compositeBuilder = new CompositeTermBuilder (new [] {"a", "b"});

            var captures = new List<Capture> ();
            var a1 = new FakeTerm ();
            captures.Add (new Capture ("a", a1));
            var a2 = new FakeTerm ();
            captures.Add (new Capture ("a", a2));
            var b1 = new FakeTerm ();
            captures.Add (new Capture ("b", b1));
            var b2 = new FakeTerm ();
            captures.Add (new Capture ("b", b2));
            var c = new FakeTerm ();
            captures.Add (new Capture ("c", c));

            var sinfo = new SourceInformation ("filename", 4, 6);
            var term = new CompositeTerm ("name", sinfo);
            compositeBuilder.Build (term, captures);

            var subTermA = term.SubTerms["a"] as ListTerm;
            Assert.IsNotNull (subTermA);
            Assert.AreEqual (new [] {a1, a2}, subTermA.Terms.ToArray ());
            Assert.AreSame (sinfo, subTermA.SourceInformation);

            var subTermB = term.SubTerms["b"] as ListTerm;
            Assert.IsNotNull (subTermB);
            Assert.AreEqual (new [] {b1, b2}, subTermB.Terms.ToArray ());
            Assert.AreSame (sinfo, subTermB.SourceInformation);

            var subTermC = term.SubTerms["c"];
            Assert.AreEqual (c, subTermC);
        }

        [Test]
        public void MultiplesShouldHaveEmptyListsIfNoCapturesEntered () {
            var builder = new CompositeTermBuilder (new[] {"a", "b"});
            var sourceInformation = new SourceInformation ("filename", 3, 5);
            var term = new CompositeTerm ("name", sourceInformation);
            builder.Build (term, new Capture[0]);

            var a = term.SubTerms["a"] as ListTerm;
            Assert.IsNotNull (a);
            Assert.AreEqual (0, a.Terms.Count);
            Assert.AreSame (sourceInformation, a.SourceInformation);

            var b = term.SubTerms["b"] as ListTerm;
            Assert.IsNotNull (b);
            Assert.AreEqual (0, b.Terms.Count);
            Assert.AreSame (sourceInformation, b.SourceInformation);
        }

        [Test]
        public void ShouldAcceptNullMultiplesAsNoMultiples () {
            var compositeBuilder = new CompositeTermBuilder (null);

            var captures = new List<Capture> ();
            var a = new FakeTerm ();
            captures.Add (new Capture ("a", a));
            var c = new FakeTerm ();
            captures.Add (new Capture ("c", c));

            var term = new CompositeTerm ("name", null);
            compositeBuilder.Build (term, captures);

            var subTermA = term.SubTerms["a"];
            Assert.AreEqual (a, subTermA);
            var subTermC = term.SubTerms["c"];
            Assert.AreEqual (c, subTermC);
        }
        [Test]
        public void ShouldHaveNameAndSourceInformation () {
            var sourceInformation = new SourceInformation ("filename", 3, 5);
            var builder = new CompositeTermBuilder (new string[0]);
            var captures = new Capture[0];
            var term = new CompositeTerm ("non-terminal", sourceInformation);
            builder.Build (term, captures);

            Assert.AreEqual ("non-terminal", term.Name);
            Assert.AreEqual (sourceInformation, term.SourceInformation);
        }

        [Test]
        public void ShouldReturnMultiplesGiven () {
            var compositeBuilder = new CompositeTermBuilder (new [] { "x", "y" });
            Assert.That (compositeBuilder.Multiples, Is.EquivalentTo (new [] { "y", "x" }));
        }

        [Test]
        public void ShouldReturnEmptyMultiplesIfNoneGiven () {
            var compositeBuilder = new CompositeTermBuilder (null);
            Assert.That (compositeBuilder.Multiples.Count (), Is.EqualTo (0));
        }
    }
}